﻿using System;
using AzukiMap.MapSource;
using AzukiMap.Tools;
using GeoAPI.Geometries;
using GisSharpBlog.NetTopologySuite.Geometries;

namespace AzukiMap.Layer
{
    public abstract class MercatorQuadLayer : TiledLayer
    {
        private const double MERCATOR_WIDTH = 40075016;
        private readonly MercatorQuadTileSource _mercatorQuadSource;

        protected MercatorQuadLayer()
        {
            
        }

        protected override int TileWidth
        {
            get { return _mercatorQuadSource.TileWidth; }
        }

        protected MercatorQuadLayer(MercatorQuadTileSource mapSource, string name)
            : base(mapSource, name, mapSource.MaxExtent)
        {
            _mercatorQuadSource = mapSource;
        }

        protected override string CreateTileReqString(double tileScale, int tileLeft, int tileTop)
        {
            throw new NotImplementedException();
        }

        protected override ICoordinate GetTileOriginFromMapCenter(IPoint point, double tileScale)
        {
            int zoomLevel = GetScaleLevel(tileScale);
            //ICoordinate pixPoint = GeoToPixel(point.X, point.Y, zoomLevel);
            ICoordinate pixPoint = GeoToPixel(point, zoomLevel);
            ICoordinate origin = GetTileOriginNum(pixPoint);

            return new Coordinate(origin.X * _mercatorQuadSource.TileWidth, origin.Y * _mercatorQuadSource.TileHeight);
        }

        protected override IPoint GetGeoPointFromTileCoord(int pixelX, int pixelY, double tileScale)
        {
            int zoomLevel = GetScaleLevel(tileScale);
            double mapSize = GetMapSize(zoomLevel);

            double lon = pixelX / mapSize * 360 - 180;
            double radY = (pixelY / mapSize - 0.5) / -1 * (2 * Math.PI);
            double lat = Math.Atan(Math.Sinh(radY)) * (180 / Math.PI);

            double x = (pixelX / mapSize) * MERCATOR_WIDTH - MERCATOR_WIDTH / 2;
            double y = (pixelY / mapSize - 0.5) * MERCATOR_WIDTH * -1;

            //return new Point(lon, lat) {SRID = 4055};
            return new Point(lon, lat) {SRID = 4326};
            //return new Point(x, y) {SRID = 3785};
            //return new Point(x, y) {SRID = 900913};
        }

        protected virtual ICoordinate GeoToPixel(IPoint geoPoint, int zoomLevel)
        {
            IPoint lonlatPoint = TransformUtil.Transform(geoPoint, 4326);
            double x = (lonlatPoint.X + 180) / 360;
            double sinLat = Math.Sin(lonlatPoint.Y * Math.PI / 180);
            double radY = 0.5 * Math.Log((1 + sinLat) / (1 - sinLat));
            double y = radY / (2 * Math.PI) * -1 + 0.5;

            int mapSize = GetMapSize(zoomLevel);
            var pixelX = (int) (x * mapSize + 0.5);
            var pixelY = (int) (y * mapSize + 0.5);

            return new Coordinate(pixelX, pixelY);
        }

        protected int GetScaleLevel(double tileScale)
        {
            return _mercatorQuadSource.ScaleLevels[tileScale];
        }

        protected virtual int GetMapSize(int zoomLevel)
        {
            return (int) (_mercatorQuadSource.TileWidth * 2 * Math.Pow(2.0, (zoomLevel - 1)));
        }

        protected ICoordinate GetTileOriginNum(ICoordinate originPoint)
        {
            int orgX = (int) originPoint.X / _mercatorQuadSource.TileWidth;
            int orgY = (int) originPoint.Y / _mercatorQuadSource.TileHeight;

            return new Coordinate(orgX, orgY);
        }

        protected override double GetTileScale(double scale)
        {
            double nearestScale = 0;
            foreach (double tileScale in _mercatorQuadSource.Scales)
            {
                if (tileScale > scale)
                {
                    nearestScale = tileScale;
                    break;
                }
            }

            if (nearestScale == 0)
            {
                nearestScale = _mercatorQuadSource.Scales[0];
            }
            return nearestScale;
        }
    }
}